package com.gengzp.permission.core.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.gengzp.permission.context.UserPrincipalContextHolder;
import com.gengzp.permission.core.PermissionCacheFunc;
import com.gengzp.permission.core.PermissionCoreFunc;
import com.gengzp.permission.exception.PermissionException;
import com.gengzp.permission.model.UserPrincipalModel;
import com.gengzp.permission.model.UserTokenModel;
import com.gengzp.permission.tools.PermissionEnvironmentVariableReader;
import com.gengzp.permission.tools.PermissionJsonConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.gengzp.permission.constants.PermissionCacheConstants.*;
import static com.gengzp.permission.constants.PermissionExceptionConstants.INTERNAL_SERVER_ERROR;

/**
 * @ClassName PermissionCoreFuncImpl
 * @Description 权限认证框架核心方法实现类
 * @Author gengzp
 * @Date 2025/8/14 22:02
 */
@Component
public class PermissionCoreFuncImpl implements PermissionCoreFunc {

    private static final Logger logger = LoggerFactory.getLogger(PermissionCoreFuncImpl.class);

    @Autowired
    private PermissionCacheFunc permissionCacheFunc;

    @Override
    public UserPrincipalModel getCacheUserPrincipalModel(String token) {
        if (token == null || token.isBlank()) {
            throw PermissionException.get(INTERNAL_SERVER_ERROR, "token为空，获取用户认证信息失败");
        }
        String userPrincipalModelStr = permissionCacheFunc.get(USER_PRINCIPAL_KEY_PREFIX + token);
        if (userPrincipalModelStr == null || userPrincipalModelStr.isBlank()) {
            throw PermissionException.get(INTERNAL_SERVER_ERROR, "用户认证信息已失效，请重新登录");
        }
        return PermissionJsonConverter.convertJsonToObject(userPrincipalModelStr,
                UserPrincipalModel.class);
    }

    @Override
    public Set<String> getAllPermissionCodesByRoleIds(Collection<String> roleIds) {
        if (roleIds == null || roleIds.isEmpty()) {
            return Collections.emptySet();
        }

        return new HashSet<>(permissionCacheFunc.getMultiKeys(
                roleIds.stream().map(v -> ROLE_PERMISSION_KEY_PREFIX + v).collect(Collectors.toList())));
    }

    @Override
    public String getSuperAdminUserId() {
        return PermissionEnvironmentVariableReader.getSuperAdminUserId();
    }

    @Override
    public String getSuperAdminUserAccount() {
        return PermissionEnvironmentVariableReader.getSuperAdminUserAccount();
    }

    @Override
    public String getSuperAdminNickname() {
        return PermissionEnvironmentVariableReader.getSuperAdminUserNickname();
    }

    @Override
    public String getSuperAdminUserPassword() {
        return PermissionEnvironmentVariableReader.getSuperAdminUserPassword();
    }

    @Override
    public String getSuperAdminUserSalt() {
        return PermissionEnvironmentVariableReader.getSuperAdminUserSalt();
    }

    @Override
    public void saveUserPrincipalModelThreadLocal(UserPrincipalModel userPrincipalModel) {
        UserPrincipalContextHolder.set(userPrincipalModel);
    }

    @Override
    public void saveUserPrincipalModelToCache(UserPrincipalModel userPrincipalModel) {
        // ===================== 1.将token对应用户信息保存到缓存 =====================
        permissionCacheFunc.set(USER_PRINCIPAL_KEY_PREFIX + userPrincipalModel.getToken(),
                PermissionJsonConverter.convertObjectToJson(userPrincipalModel),
                PermissionEnvironmentVariableReader.getTokenDuration(), TimeUnit.SECONDS);

        // ===================== 2.将用户对应token集合保存到缓存 =====================
        // (需要从缓存中查出当前用户所有在线token, 根据客户端类型, 顶替掉相同客户端类型的token)
        Set<UserTokenModel> tokenModelsSet = new HashSet<>();
        String tokenSetStr = permissionCacheFunc.get(USER_TOKEN_KEY_PREFIX + userPrincipalModel.getUserId());
        if (tokenSetStr != null
                && !tokenSetStr.isBlank()
                && userPrincipalModel.getClientType() != null
                && !userPrincipalModel.getClientType().isBlank()) {
            tokenModelsSet = PermissionJsonConverter
                    .convertJsonToGenericObject(tokenSetStr, new TypeReference<Set<UserTokenModel>>() {
                    }).stream().filter(v ->
                            v.getClientType() == null || !userPrincipalModel.getClientType().equals(v.getClientType()))
                    .collect(Collectors.toSet());
        }
        tokenModelsSet.add(new UserTokenModel(userPrincipalModel.getToken(), userPrincipalModel.getClientType()));
        permissionCacheFunc.set(USER_TOKEN_KEY_PREFIX + userPrincipalModel.getUserId(),
                PermissionJsonConverter.convertObjectToJson(tokenModelsSet),
                PermissionEnvironmentVariableReader.getTokenDuration(), TimeUnit.SECONDS);
    }

    @Override
    public void clearUserPrincipalModelThreadLocal() {
        UserPrincipalContextHolder.clear();
    }

    @Override
    public UserPrincipalModel login(String userId, String clientType, Collection<String> roleIds) throws PermissionException {
        try {
            // 使用 SHA-256 哈希算法处理 userId, uuid, 当前时间戳 拼接得到的字符串, 确保token长度固定且包含所有信息的特征
            byte[] hash = MessageDigest
                    .getInstance("SHA-256")
                    .digest((String.format("%s%s%s",
                            userId,
                            UUID.randomUUID().toString().replace("-", ""),
                            System.currentTimeMillis()).getBytes(StandardCharsets.UTF_8)));

            // 转换为十六进制字符串
            StringBuilder tokenBuilder = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    tokenBuilder.append('0');
                }
                tokenBuilder.append(hex);
            }

            // 将组装完成的字符串作为 token
            String token = tokenBuilder.toString();

            // 组装用户登录信息, 保存到缓存中
            UserPrincipalModel userPrincipalModel = new UserPrincipalModel();
            userPrincipalModel.setUserId(userId);
            userPrincipalModel.setClientType(clientType);
            userPrincipalModel.setRoleIds(
                    (roleIds != null && !roleIds.isEmpty()) ? new ArrayList<>(roleIds) : Collections.emptyList());
            userPrincipalModel.setToken(token);

            // 将用户认证信息保存到缓存中
            saveUserPrincipalModelToCache(userPrincipalModel);

            return userPrincipalModel;
        } catch (Exception e) {
            logger.error("用户登录失败", e);
            throw PermissionException.get(INTERNAL_SERVER_ERROR, String.format("登录失败：%s", e.getMessage()));
        }
    }

    @Override
    public void quit(String userId, String token) throws PermissionException {
        try {
            // 1.删除token对应用户信息缓存
            permissionCacheFunc.del(USER_PRINCIPAL_KEY_PREFIX + token);

            // 2.获取当前用户对应token集合缓存
            String existTokenModelSetStr = permissionCacheFunc.get(USER_TOKEN_KEY_PREFIX + userId);
            if (existTokenModelSetStr == null || existTokenModelSetStr.isBlank()) {
                return;
            }

            // 3.剔除掉要退出的token
            List<UserTokenModel> finalUserTokenModels = PermissionJsonConverter.convertJsonToGenericObject(
                    existTokenModelSetStr, new TypeReference<Set<UserTokenModel>>() {
                    }).stream().filter(v -> !v.getToken().equals(token)).toList();
            if (finalUserTokenModels.isEmpty()) {
                return;
            }

            // 4.重新保存token集合, 并且用先前的剩余时间作为过期时间
            permissionCacheFunc.set(USER_TOKEN_KEY_PREFIX + userId,
                    PermissionJsonConverter.convertObjectToJson(finalUserTokenModels),
                    permissionCacheFunc.getExpire(USER_TOKEN_KEY_PREFIX + userId, TimeUnit.SECONDS), TimeUnit.SECONDS);
        } catch (Exception e) {
            logger.error("用户认证信息删除失败", e);
            throw PermissionException.get(INTERNAL_SERVER_ERROR, String.format("用户认证信息删除失败：%s", e.getMessage()));
        }
    }

    @Override
    public void kickOut(String userId) throws PermissionException {
        try {
            // 1.查询当前用户在线的所有token
            String existTokenModelSetStr = permissionCacheFunc.get(USER_TOKEN_KEY_PREFIX + userId);
            if (existTokenModelSetStr == null || existTokenModelSetStr.isBlank()) {
                return;
            }

            // 2.获取所有要删除的缓存key
            Set<String> delKeys = PermissionJsonConverter.convertJsonToGenericObject(
                    existTokenModelSetStr, new TypeReference<Set<UserTokenModel>>() {
                    }).stream().map(UserTokenModel::getToken).collect(Collectors.toSet());
            delKeys.add(USER_TOKEN_KEY_PREFIX + userId);

            // 3.删除将所有token缓存
            permissionCacheFunc.del(delKeys);
        } catch (Exception e) {
            logger.error("用户认证信息删除失败", e);
            throw PermissionException.get(INTERNAL_SERVER_ERROR, String.format("用户认证信息删除失败：%s", e.getMessage()));
        }
    }

    @Override
    public UserPrincipalModel getCurrentUserPrincipalModelFromThreadLocal() {
        return UserPrincipalContextHolder.get();
    }

    @Override
    public Set<String> getCurrentUserOwnedPermissionCodes() throws PermissionException {
        try {
            Set<String> permissionCodes = new HashSet<>();

            // 获取当前用户的所有角色id
            Set<String> roleIds = new HashSet<>(getCurrentUserPrincipalModelFromThreadLocal().getRoleIds());
            if (roleIds.isEmpty()) {
                return permissionCodes;
            }

            // 查询所有角色在缓存中拥有的权限, 添加到角色权限集合中
            Optional.ofNullable(getRolePermissionCodes(roleIds)).ifPresent(permissionCodes::addAll);

            return permissionCodes;
        } catch (Exception e) {
            logger.error("获取当前用户权限信息失败", e);
            throw PermissionException.get(INTERNAL_SERVER_ERROR, String.format("获取当前用户权限信息失败：%s", e.getMessage()));
        }
    }

    @Override
    public void assignCacheRolePermissions(String roleId, Collection<String> permissionCodes, boolean isReplace) throws PermissionException {
        try {
            // 将角色id统一存储到缓存中
            Set<String> existRoleIds = new HashSet<>();
            String existRoleIdsStr = permissionCacheFunc.get(ROLE_ID_KEY_PREFIX);
            if (existRoleIdsStr != null && !existRoleIdsStr.isBlank()) {
                existRoleIds = new HashSet<>(List.of(existRoleIdsStr.split(",")));
            }
            existRoleIds.add(roleId);
            permissionCacheFunc.set(ROLE_ID_KEY_PREFIX, String.join(",", existRoleIds));

            // 将角色权限关联关系保存到缓存中
            Set<String> existCachePermissionCodes = new HashSet<>();
            if (!isReplace) {
                String existCachePermissionCodesStr = permissionCacheFunc.get(ROLE_PERMISSION_KEY_PREFIX + roleId);
                if (existCachePermissionCodesStr != null && !existCachePermissionCodesStr.isBlank()) {
                    existCachePermissionCodes = new HashSet<>(List.of(existCachePermissionCodesStr.split(",")));
                }
            }
            existCachePermissionCodes.addAll(permissionCodes);
            permissionCacheFunc.set(ROLE_PERMISSION_KEY_PREFIX + roleId, String.join(",", existCachePermissionCodes));
        } catch (Exception e) {
            logger.error("更新角色权限缓存数据失败", e);
            throw PermissionException.get(INTERNAL_SERVER_ERROR, String.format("更新角色权限缓存数据失败：%s", e.getMessage()));
        }
    }

    @Override
    public void assignCacheUserRoles(String userId, Collection<String> roleIds, boolean isReplace) throws PermissionException {
        try {
            // 查询出所有在线的用户token
            String existTokenModelSetStr = permissionCacheFunc.get(USER_TOKEN_KEY_PREFIX + userId);
            if (existTokenModelSetStr == null || existTokenModelSetStr.isBlank()) {
                return;
            }
            Set<String> existTokenCacheKeys = PermissionJsonConverter.convertJsonToGenericObject(
                    existTokenModelSetStr, new TypeReference<Set<UserTokenModel>>() {
                    }).stream().map(v -> USER_PRINCIPAL_KEY_PREFIX + v.getToken()).collect(Collectors.toSet());

            // 根据token查询出所有在线的用户信息缓存数据
            List<String> existCacheUserPrincipalModelsStr = permissionCacheFunc.getMultiKeys(existTokenCacheKeys);
            if (existCacheUserPrincipalModelsStr == null || existCacheUserPrincipalModelsStr.isEmpty()) {
                return;
            }
            List<UserPrincipalModel> userPrincipalModels = existCacheUserPrincipalModelsStr.stream().map(v ->
                    PermissionJsonConverter.convertJsonToObject(v, UserPrincipalModel.class)).toList();

            // 更改用户拥有的角色id
            for (UserPrincipalModel userPrincipalModel : userPrincipalModels) {
                Set<String> existRoleIds = new HashSet<>();
                if (!isReplace) {
                    existRoleIds = new HashSet<>(userPrincipalModel.getRoleIds());
                }
                if (roleIds != null && !roleIds.isEmpty()) {
                    existRoleIds.addAll(roleIds);
                    userPrincipalModel.setRoleIds(new ArrayList<>(existRoleIds));
                }

                // 重新缓存数据, 且不更改过期时间
                permissionCacheFunc.set(USER_PRINCIPAL_KEY_PREFIX + userPrincipalModel.getToken(),
                        PermissionJsonConverter.convertObjectToJson(userPrincipalModel),
                        permissionCacheFunc.getExpire(USER_PRINCIPAL_KEY_PREFIX + userPrincipalModel.getToken(), TimeUnit.SECONDS),
                        TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            logger.error("更新用户角色缓存数据失败", e);
            throw PermissionException.get(INTERNAL_SERVER_ERROR, String.format("更新用户角色缓存数据失败：%s", e.getMessage()));
        }
    }

    @Override
    public void removeCacheRole(String roleId) throws PermissionException {
        try {
            // 删除角色id
            String existRoleIdStr = permissionCacheFunc.get(ROLE_ID_KEY_PREFIX);
            if (existRoleIdStr != null && !existRoleIdStr.isBlank()) {
                Set<String> existRoleIds = new HashSet<>(List.of(existRoleIdStr.split(",")));
                existRoleIds.remove(roleId);
                permissionCacheFunc.set(ROLE_ID_KEY_PREFIX,
                        existRoleIds.isEmpty() ? "" : String.join(",", existRoleIds));
            }
            // 删除角色权限关联关系
            permissionCacheFunc.del(ROLE_PERMISSION_KEY_PREFIX + roleId);
        } catch (Exception e) {
            logger.error("删除角色缓存数据失败", e);
            throw PermissionException.get(INTERNAL_SERVER_ERROR, String.format("删除角色缓存缓存数据失败：%s", e.getMessage()));
        }
    }

    @Override
    public void removeAllCacheRoles() throws PermissionException {
        try {
            // 查出缓存中存在的所有角色id
            String existRoleIdStr = permissionCacheFunc.get(ROLE_ID_KEY_PREFIX);
            if (existRoleIdStr == null || existRoleIdStr.isBlank()) {
                return;
            }

            // 根据角色id批量删除角色权限关联关系
            permissionCacheFunc.del(
                    Stream.of(existRoleIdStr.split(",")).map(v -> ROLE_PERMISSION_KEY_PREFIX + v).toList());

            // 删除角色id
            permissionCacheFunc.del(ROLE_ID_KEY_PREFIX);
        } catch (Exception e) {
            logger.error("删除角色缓存数据失败", e);
            throw PermissionException.get(INTERNAL_SERVER_ERROR, String.format("删除角色缓存缓存数据失败：%s", e.getMessage()));
        }
    }

    @Override
    public Set<String> getRolePermissionCodes(String roleId) throws PermissionException {
        if (roleId == null || roleId.isBlank()) {
            return Collections.emptySet();
        }

        String permissionModelsStr = permissionCacheFunc.get(ROLE_PERMISSION_KEY_PREFIX + roleId);
        if (permissionModelsStr == null || permissionModelsStr.isBlank()) {
            return Collections.emptySet();
        }

        return new HashSet<>(List.of(permissionModelsStr.split(",")));
    }

    @Override
    public Set<String> getRolePermissionCodes(Collection<String> roleIds) throws PermissionException {
        if (roleIds == null || roleIds.isEmpty()) {
            return Collections.emptySet();
        }

        List<String> permissionCodesList = permissionCacheFunc.getMultiKeys(
                roleIds.stream().map(v -> ROLE_PERMISSION_KEY_PREFIX + v).toList());
        if (permissionCodesList == null || permissionCodesList.isEmpty()) {
            return Collections.emptySet();
        }

        return permissionCodesList.stream().map(v -> v.split(",")).flatMap(Arrays::stream).collect(Collectors.toSet());
    }

    @Override
    public List<String> getAllCacheRoleIds() {
        String roleIdsStr = permissionCacheFunc.get(ROLE_ID_KEY_PREFIX);
        if (roleIdsStr == null || roleIdsStr.isBlank()) {
            return new ArrayList<>();
        }
        return new ArrayList<>(List.of(roleIdsStr.split(",")));
    }

}
